package independence.reflection
{
	import independence.core.IndyObject;
	
	/**
	 * The Property class represents a property on an object: any method utilizing
	 * the <code>get</code> or <code>set</code> statements to appear as a property. 
	 * 
	 * @author TK, TK Assembled for Independence Glory.
	 */
	public final class Property extends IndyObject implements IReadableMember, IWritableMember
	{
		/**
		 * @protected The class which defines this property.
		 */
		protected var clazz:Class;
		/**
		 * @protected The name of the property.
		 */
		protected var propertyName:String;
		/**
		 * @protected The class type of the property.
		 */
		protected var type:Class;
		/**
		 * @protected The access rights to this property. See PropertyAccess's constants.
		 */
		protected var access:String;
		/**
		 * @protected Whether this property is static or not.
		 */
		protected var statik:Boolean;
		/**
		 * Constructs a new Property object.
		 * @param	clazz			The class which declares this property.
		 * @param	propertyName	The String name of this property.
		 * @param	type			The Class type of the property.
		 * @param	access			The access rights to this property: see PropertyAccess's constants.
		 * @param	statik			Whether this property is class-level(static) or instance-level(not static).
		 */
		public function Property(clazz:Class, propertyName:String, type:Class, access:String, statik:Boolean = false)
		{
			this.clazz = clazz;
			this.propertyName = propertyName;
			this.type = type;
			this.access = access;
			this.statik = statik;
		}
		/**
		 * Returns whether or not this property is static.
		 * @return	True if this property is static, false otherwise.
		 */
		public function isStatic():Boolean
		{
			return this.statik;
		}
		/**
		 * Returns the Class object which declares this property.
		 * @return	The Class object which declares this property.
		 */
		public function getDeclaringClass():Class
		{
			return this.clazz;
		}
		/**
		 * Returns the String name of this property.
		 * @param	The String name of this property.
		 */
		public function getName():String
		{
			return this.propertyName;
		}
		/**
		 * Returns the class type of this property.
		 * @return	The class type of this property.
		 */
		public function getType():Class
		{
			return this.type;
		}
		/**
		 * Returns the access rights to this property. See PropertyAccess's constants.
		 * @param	This property's access rights: PropertyAccess.READ, PropertyAccess.WRITE, or PropertyAccess.READ_WRITE.
		 */
		public function getAccessRights():String
		{
			return this.access;
		}
		/**
		 * Gets the value of this property in the <code>target</code> object.
		 * @param	target	The target object of which to get the value of this property.
		 * @return	The value of this property in the <code>target</code> object.
		 * @throws	PropertyAccessError	If accessing a write-only property, a PropertyAccessError is thrown.
		 */
		public function getValue(target:Object):Object 
		{
			if (this.access == PropertyAccess.READ || this.access == PropertyAccess.READ_WRITE) {
				return (statik ? clazz[propertyName] : target[propertyName]);
			} else {
				throw new PropertyAccessError(this.propertyName, String(clazz), PropertyAccess.READ);
				return null;
			}
		}
		/**
		 * Get the Array value of this property in the <code>target</code> object.
		 * @param	target	The target object of which to get the value of this property.
		 * @return	The Array value of this property in the <code>target</code> object.
		 * @throws	PropertyAccessError	If accessing a write-only property, a PropertyAccessError is thrown.
		 */
		public function getArray(target:Object):Array 
		{
			return this.getValue(target) as Array;
		}
		/**
		 * Gets the boolean value of this property in the <code>target</code> object.
		 * @param	target	The target object of which to get the value of this property.
		 * @return	The boolean value of this property in the <code>target</code> object.
		 * @throws	PropertyAccessError	If accessing a write-only property, a PropertyAccessError is thrown.
		 */
		public function getBoolean(target:Object):Boolean
		{
			return Boolean(getValue(target));
		}
		/**
		 * Gets the integer value (int) of this property in the <code>target</code> object.
		 * @param	target	The target object of which to get the value of this property.
		 * @return	The integer value of this property in the <code>target</code> object.
		 * @throws	PropertyAccessError	If accessing a write-only property, a PropertyAccessError is thrown.
		 */
		public function getInteger(target:Object):int
		{
			return int(getValue(target));
		}
		/**
		 * @inheritDoc
		 */
		public function getNumber(target:Object):Number
		{
			return Number(getValue(target));
		}
		/**
		 * Gets the string value of this property in the <code>target</code> object.
		 * @param	target	The target object of which to get the value of this property.
		 * @return	The string value of this property in the <code>target</code> object.
		 * @throws	PropertyAccessError	If accessing a write-only property, a PropertyAccessError is thrown.
		 */
		public function getString(target:Object):String
		{
			return String(getValue(target));
		}
		/**
		 * Gets the unsigned integer (uint) value of this property in the <code>target</code> object.
		 * @param	target	The target object of which to get the value of this property.
		 * @return	The unsigned integer value of this property in the <code>target</code> object.
		 * @throws	PropertyAccessError	If accessing a write-only property, a PropertyAccessError is thrown.
		 */
		public function getUnsignedInt(target:Object):uint
		{
			return uint(getValue(target));
		}
		/**
		 * Gets the XML value of this property in the <code>target</code> object.
		 * @param	target	The target object of which to get the value of this property.
		 * @return	The XML value of this property in the <code>target</code> object.
		 * @throws	PropertyAccessError	If accessing a write-only property, a PropertyAccessError is thrown.
		 */
		public function getXML(target:Object):XML
		{
			return XML(getValue(target));
		}
		/**
		 * Sets the value of this property in the <code>target</code> object.
		 * @param	target	The target object of which to set the value of this property.
		 * @param	value	The value to set the property to.
		 * @throws	PropertyAccessError	If the property is read-only, a PropertyAccessError is thrown.
		 */
		public function setValue(target:Object, value:Object):void
		{
			if (this.access == PropertyAccess.WRITE || this.access == PropertyAccess.READ_WRITE) {
				statik ? clazz[propertyName] = value : target[propertyName] = value;
			} else {
				throw new PropertyAccessError(propertyName, String(clazz), PropertyAccess.WRITE);
			}
		}
		/**
		 * Sets the array value of this property in the <code>target</code> object.
		 * @param	target	The target object of which to set the value of this property.
		 * @param	value	The value to set the property to.
		 * @throws	PropertyAccessError	If the property is read-only, a PropertyAccessError is thrown.
		 */		
		public function setArray(target:Object, value:Array):void
		{
			this.setValue(target, value);
		}	
		/**
		 * Sets the boolean value of this property in the <code>target</code> object.
		 * @param	target	The target object of which to set the value of this property.
		 * @param	value	The value to set the property to.
		 * @throws	PropertyAccessError	If the property is read-only, a PropertyAccessError is thrown.
		 */		
		public function setBoolean(target:Object, value:Boolean):void
		{
			this.setValue(target, value);
		}	
		/**
		 * Sets the integer value of this property in the <code>target</code> object.
		 * @param	target	The target object of which to set the value of this property.
		 * @param	value	The value to set the property to.
		 * @throws	PropertyAccessError	If the property is read-only, a PropertyAccessError is thrown.
		 */		
		public function setInteger(target:Object, value:int):void
		{
			this.setValue(target, value);	
		}
		/** 
		 * @inheritDoc
		 */
		public function setNumber(target:Object, value:Number):void
		{
			this.setValue(target, value);
		}
		/**
		 * Sets the string value of this property in the <code>target</code> object.
		 * @param	target	The target object of which to set the value of this property.
		 * @param	value	The value to set the property to.
		 * @throws	PropertyAccessError	If the property is read-only, a PropertyAccessError is thrown.
		 */		
		public function setString(target:Object, value:String):void
		{
			this.setValue(target, value);
		}
		/**
		 * Sets the unsigned integer value of this property in the <code>target</code> object.
		 * @param	target	The target object of which to set the value of this property.
		 * @param	value	The value to set the property to.
		 * @throws	PropertyAccessError	If the property is read-only, a PropertyAccessError is thrown.
		 */		
		public function setUnsignedInt(target:Object, value:uint):void
		{
			this.setValue(target, value);
		}
		/**
		 * Sets the XML value of this property in the <code>target</code> object.
		 * @param	target	The target object of which to set the value of this property.
		 * @param	value	The value to set the property to.
		 * @throws	PropertyAccessError	If the property is read-only, a PropertyAccessError is thrown.
		 */		
		public function setXML(target:Object, value:XML):void
		{
			this.setValue(target, value);
		}
		/**
		 * Returns a String representation of this <code>Property</code> object.
		 * @return	A String representation of this <code>Property</code>.
		 */
		public function toString():String
		{
			return "[Property " + this.propertyName + "]";
		}
	}
}